package bg.tu.varna.pms.model.communication;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;

import bg.tu.varna.pms.model.Contact;

/**
 * Sync messages are meant to provide client with fresh data about its contact
 * list, messages and files. Other properties are not refreshed
 * 
 * @author Gospodin Gospodinov
 */
public final class SyncMessage {
	private SyncMessage() {
	}

	/**
	 * Shows the element that should be synchronized. There are couple of
	 * possibilities like contact list, chat and file messages. First one is
	 * send as response while others need to be delivered with respective
	 * messages.
	 * 
	 * @author Gospodin Gospodinov
	 */
	public static enum SyncType {
		/**
		 * Contact list is meant to be delivered with SyncMessage.Response
		 * object.
		 */
		ContactList,
		/**
		 * Chat messages have to be send with ChatMessage.Request.
		 */
		ChatMessages,
		/**
		 * File messages have to be send with FileMessage.Request.
		 */
		FileMessages,
		/**
		 * This is for enemy confusion.
		 */
		General
	}

	/**
	 * Operation which must be performed on client side for the sync to work.
	 * This is available for contact list only. Other sync types are handled by
	 * receiving of message and file.
	 * 
	 * @author Gospodin Gospodinov
	 */
	public static enum OperationType {
		Add, Remove, Status
	}

	/**
	 * Synchronization request. The message carry required synchronization type
	 * and last sync time according to local cache settings.
	 * 
	 * @author Gospodin Gospodinov
	 */
	public final static class Request implements Serializable {
		private static final long serialVersionUID = -457279719373276910L;
		private final SyncType mSyncType;
		private final Calendar mLastSync;

		/**
		 * Create sync request object.
		 * 
		 * @param syncType
		 *            Type of synchronization request.
		 * @param lastSync
		 *            Last synchronization based on local user cache.
		 */
		public Request(SyncType syncType, Calendar lastSync) {
			mSyncType = syncType;
			mLastSync = lastSync;
		}

		/**
		 * Create full sync request object.
		 * 
		 * @param syncType
		 *            Type of synchronization request.
		 */
		public Request(SyncType syncType) {
			mSyncType = syncType;
			mLastSync = null;
		}

		/**
		 * Get synchronization type requested.
		 * 
		 * @return Synchronization type.
		 */
		public SyncType getSyncType() {
			return mSyncType;
		}

		/**
		 * Get last synchronization based on local user cache.
		 * 
		 * @return Last synchronization.
		 */
		public Calendar getLastSync() {
			return mLastSync;
		}
	}

	/**
	 * Response message carry all changes on user's contact list without
	 * friendship requests. They are sent as response to SyncMessage.Request but
	 * through different message.
	 * 
	 * @author Gospodin Gospodinov
	 */
	public final static class Response implements Serializable {
		private static final long serialVersionUID = 3227430205397770123L;
		private final HashMap<OperationType, List<Contact>> mContactListSyncronizations;

		/**
		 * Generate response object based on add, remove and status changes on
		 * contact list. Elements can be duplicated in first and last list.
		 * 
		 * @param addContacts
		 *            List of new contacts.
		 * @param removeContacts
		 *            List of removed contacts.
		 * @param statusContacts
		 *            List of status changes.
		 */
		public Response(Contact[] addContacts, Contact[] removeContacts,
				Contact[] statusContacts) {
			mContactListSyncronizations = new HashMap<>();
			if (addContacts != null && addContacts.length > 0) {
				mContactListSyncronizations.put(OperationType.Add,
						Arrays.asList(addContacts));
			}
			if (removeContacts != null && removeContacts.length > 0) {
				mContactListSyncronizations.put(OperationType.Remove,
						Arrays.asList(removeContacts));
			}
			if (statusContacts != null && statusContacts.length > 0) {
				mContactListSyncronizations.put(OperationType.Status,
						Arrays.asList(statusContacts));
			}
		}

		/**
		 * Get list of new contacts.
		 * 
		 * @return List of new contacts.
		 */
		public List<Contact> getAddedContacts() {
			return mContactListSyncronizations.containsKey(OperationType.Add) ? mContactListSyncronizations
					.get(OperationType.Add) : null;
		}

		/**
		 * Get list of removed contacts.
		 * 
		 * @return List of removed contacts.
		 */
		public List<Contact> getRemovedContacts() {
			return mContactListSyncronizations
					.containsKey(OperationType.Remove) ? mContactListSyncronizations
					.get(OperationType.Remove) : null;
		}

		/**
		 * Get list of status changes. The objects is eligable for contacts
		 * which have friendship acceptance and are not offline or incognito.
		 * 
		 * @return List of status changes.
		 */
		public List<Contact> getChangedContacts() {
			return mContactListSyncronizations
					.containsKey(OperationType.Status) ? mContactListSyncronizations
					.get(OperationType.Status) : null;
		}
	}
}
